home *** CD-ROM | disk | FTP | other *** search
Text File | 1994-12-04 | 37.2 KB | 1,323 lines | [TEXT/KAHL] |
-
- //• C translation from Pascal source file: Missile.p
-
- //• From: mrob & world. std. com (Robert P Munafo).
- //• Subject : "Missile " source from 1984.
-
- //• Recently in comp. sys. mac. programmer there has been some discussion.
- //• of early Macintosh programs that still run on current machines ,.
- //• and my Missile Command game from mid - 1984 was mentioned.
-
- //• This program was written in Lisa Pascal,.
- //• the cross - development system that everyone had to use.
- //• until Stanford's sumacc came along,.
- //• and has the rather charming property of continuing to run on.
- //• all new machines that Apple comes out with.
- //• I ran it on my own Power Mac 6100 / 60 yesterday just for fun.
- //• It 's just as playable as it was on the old 128K Mac.
- //• (although the timing for the " Game Over " screen is now way off ,.
- //• as I didn 't check TickCount for that).
-
- //• I have not done anything to this code ,.
- //• like try to port it to any recent Pascal compiler or anything like that.
- //• I know that Lisa Pascal , Macintosh Pascal ,.
- //• and TML Language Systems Pascal were all reasonably.
- //• compatible with each other;.
- //• after that I switched over to Think C and lost track.
-
- //• The "%title " and " %subtitle " directives were.
- //• for the print spooler on the Dartmouth mainframe that I used to.
- //• print out listings.
-
- //• There seem to be a lot more options in the Game Options dialog.
- //• than the actual Missile game provides.
- //• I 'm not sure what' s going on with that.
- //• - - - - - - - - - - - Cut Here - - - Pascal Source code - - - - - - - - - - -.
-
-
- //• %title 'Missile - a game for the Apple Macintosh'.
- //• %subtitle 'Overall program description'.
- //• $X-.
- //• Turns off A.R.s.E.
-
- //• main Game;
-
- //• Missile -- A Missile Command game for the Macintosh.
- //• by Robert P. Munafo at Dartmouth College.
-
- //• I wrote this to learn a bit about Mac programming. It has a.
- //• few 'standard user interface' bugs:.
-
- //• It doesn't update the window during the 'End of round X' and.
- //• 'GAME OVER' routines. (It only updates while playing.).
- //• It won't quit when you select "quit" while a desk accessory is.
- //• on screen. Instead, it will wait until you close all the.
- //• desk accessories, then quit.
- //• The cursor looks the same all the time. It should change.
- //• shape to let the user know when he can and cannot fire missiles.
-
- //• It also has some missing features (which I hope to add soon):.
-
- //• o Smart Bombs.
- //• o Add an "Ammunition: [xxx]% of normal" item to the dialog box.
- //• o Sound.
- //• o User should be able to pick what round the game starts with.
- //• o High scores list saved on the disk.
- //• o Two-player option.
- //• o The animation needs to be made faster (in some manner).
-
-
- //• %subtitle 'USES statements'.
-
- #include <Packages.h>
-
- //• %subtitle 'Constants'.
- enum {
- lastMenu = 4, //• number of menus.
- appleMenu = 1, //• menu ID for desk accessory menu.
- fileMenu = 256, //• menu ID for File menu.
- editMenu = 257, //• menu ID for the Edit menu.
- gameMenu = 258, //• menu ID for Game menu.
- str_1_ID = 300, //• ID for my first string of text.
- str_2_ID = 350, //• ID for my second string of text.
- box_ID = 450, //• ID of "about" dialog box.
- box2_id = 451, //• ID of options box.
-
- fbIdle = 0,
- fbGrow = 1,
- fbShrink = 2,
- maxFb = 60, //• maximum number of fireballs allowed.
- fbRad = 15, //• Radius of fireballs.
- fbRadRate = 2, //• Rate of expansion/contraction of fireballs.
- msIdle = 0,
- msActive = 1,
- msNormal = 1,
- msMirv = 2,
- msSmart = 3,
- maxMs = 20, //• maximum number of missiles allowed.
- mWidth = 3, //• Width of missile tracks.
- cityHeight = 40, //• Distance from bottom up to cities.
- cityWidth = 50, //• Width of cities.
- BuildHeight = 30, //• maximum height of buildings.
- buildWidth = 4, //• Width of buildings.
- nCities = 6 //• Number of cities.
- };
-
- typedef struct { //• Data type to describe a fireball.
- Rect bounds; //• Rectanggle to copy bits into.
- short size; //• Current radius of fireball.
- short mode; //• Idle, Growing, or shrinking.
- } fireball;
-
- typedef struct { //• Data type to describe a missile.
- Point start; //• One endpoint of the missile's path.
- Point pos; //• Position within path - 0 to 1.
- short dh, dv; //• Vertical and horizontal speed in pixels.
- short city; //• Number of the target city.
- short kind; //• MIRV or normal.
- short mode; //• Idle or Active.
- } missile;
-
-
- MenuHandle myMenus[lastMenu];
- Rect screenRect;
- Boolean doneFlag;
- EventRecord myEvent;
- short code, refNum;
- WindowRecord wRecord;
- WindowPtr myWindow, whichWindow;
- StringHandle first_string, second_string; //• Handles to strings of text for AboutMissile.
-
- //• My game variables are here.
- BitMap fbBitMap; //• BitMap for fireball pictures.
- short fbBits[3600]; //• Actual bits for fireball pictures.
-
- Cursor crosshairs; //• Crosshairs cursor read in from resource.
- Cursor crosshairs2;
- CursHandle hCurs, hCurs2; //• Handle to the cursor.
- PatHandle cityPattern;//• Handle to pattern in which to draw cities.
- short fieldWidth; //• Width of the playing field.
- short fieldHeight; //• Height of the playing field.
- short i; //• Loop variable.
- fireball fbs[maxFb]; //• Each entry describes one fireball.
- missile missiles[maxMs]; //• Each entry describes one missile.
- short nMissiles; //• Number of currently active missiles.
- long lastTick; //• System clock at time of previous update.
- Boolean gameOver; //• Set true when all cities are destroyed.
- Boolean pauseFlag; //• True when game is being paused.
- Boolean playing; //• True except buring bonus points and GAME OVER screens.
- Boolean esFlag; //• True if "GAME OVER" is to be drawn after exiting main loop.
- short endDelay; //• Used to wait a while after end of round.
- short citiesLeft; //• Number of cities still alive.
- short bCities; //• Number of bonus cities they have left.
- Boolean cities[nCities]; //• State of each city : false = destroyed.
- Boolean cities2[nCities]; //• State of the city before the round began.
- short nukeHeight; //• Vertical position of fireball required to destroy city.
- short endv; //• Y-coordinate of the end of a missile's path.
- short mirvRate;
- short mirvHeight; //• Height at which Mirvs MIRV.
- short mirvNasty; //• Percent chance of sub-missile on mirv for each city.
- short msSpeed; //• Vertical speed of missiles, in 1/16ths of a pixel.
- short msRate; //• Percent rate of missile production.
- short roundNumber; //• Number of the current round.
- long score; //• The player's current score.
- long disScore; //• score currently displayed on the screen.
- long highScore; //• The current highest score.
- short RoundH;
- short scoreH; //• Horiz. position of text 'score:'.
- short scoreNumH; //• Horiz. position of score.
- short statsH; //• Horiz. position of text 'Enemy left:'.
- short statsNumH;
- Rect statsRect;
- short eLeft; //• How many enemy missiles are left this round.
- short yLeft; //• How many missiles you (the player) have left.
- short eDestroyed; //• Number of enemy missiles destroyed.
- short eDesH; //• Horiz. position of text 'Enemy Destroyed:'.
- short eDesNumH; //• Horiz. position of # of enemy missiles destroyed.
-
- short gameSpeed; //• Number of ticks between each AdvanFb call.
- short startRound; //• Round to start off with.
- Boolean mFlag1; //• Do missiles aim for dead cities?.
- short mFlag2; //• Extent to which enemy missiles blow up.
- Boolean MExists[3]; //• Does this type of missile occur?.
- short MPBase[3]; //• Value of missile at first round.
- short MPExtra[3]; //•.plus this much each additional round.
- short MPoints[3]; //•.is this much in all.
-
- void SetUpMenus (void);
- void InitFbs (void);
- void SetUp (void);
- void AboutMissile (void);
- Fixed Rnd (Fixed n);
- void DrawNumber (long n);
- void UpdScore (void);
- void DrawScore (void);
- void BottomLine (void);
- void AllocFB (Point location);
- void AdvanFb (void);
- void AllocMS (Point position, short city, short kind);
- Boolean BlackPixel (Point aPoint);
- void AdvanMs (void);
- void Centre (Str255 theText, short posV);
- void ClearScreen (void);
- void DrawCity (short city);
- void DrawStuff (void);
- short MinMax (Str255 theString, short minimum, short maximum);
- void DoGameOptions (void);
- void DoCommand (long mResult);
- void CheckEvents (void);
- void EndScreen (void);
- void InitRound (void);
- void EndBonus (void);
- void PlayGame (void);
- void main (void);
-
- //• %subtitle 'Init routine for menus'.
- void SetUpMenus ()
- {
- short i;
-
- myMenus[1] = GetMenu (appleMenu); //• Load menu from disk.
- //• MyMenus[1]^^.MenuData[1] := Chr (AppleSymbol);.
- //• Change title to the Apple symbol.
- AddResMenu (myMenus[1], 'DRVR'); //• Add items for desk accessories.
- myMenus[2] = GetMenu (fileMenu);
- myMenus[3] = GetMenu (editMenu);
- myMenus[4] = GetMenu (gameMenu);
- for (i = 1; i <= lastMenu; i++)
- InsertMenu (myMenus[i], 0);
- DrawMenuBar ();
- }
- //• of SetUpMenus.
-
- //• %subtitle 'Init routine for fireball BitMaps'.
- //• This routine is called by the main Init routine, below. It creates a.
- //• bunch of bit images of circles off-screen, which can later be drawn on.
- //• the screen much faster than FrameOval.
- void InitFbs ()
- {
- Fixed i;
- Rect aRect; //• Rectangle for drawing FrameOvals.
- BitMap saveBits; //• To save the current GrafPort while drawing off-screen.
- Rect saveRect;
-
- saveBits = qd.thePort->portBits;
- saveRect = qd.thePort->portRect;
- fbBitMap.rowBytes = 8;
- SetRect (&fbBitMap.bounds, 0, 0, 60, 60);
- qd.thePort->portRect = fbBitMap.bounds;
-
- PenSize (fbRadRate, fbRadRate);
- PenPat (&qd.black);
- PenMode (patCopy);
- SetRect (&aRect, 30, 30, 30, 30); //• Start with an empty rectangle in the center.
- for (i = 0; i < 14; i++)
- {
- fbBitMap.baseAddr = (Ptr) &fbBits[240 * i + 1]; //• Pick starting address for this picture.
- InsetRect (&aRect, -2, -2); //• Make rect a bit larger.
- SetPortBits (&fbBitMap);
- EraseRect (&qd.thePort->portBits.bounds); //• Erase to all white.
- FrameOval (&aRect); //• Draw the circle.
- }
- SetPortBits (&saveBits); //• Return to normal screen drawing.
- qd.thePort->portRect = saveRect;
- }
- //• of InitFbs.
-
- //• %subtitle 'Init routine'.
- void SetUp ()
- {
- Rect wRect; //• Window Rectangle.
- short i;
- //• Macintosh System initialization.
- InitGraf (&qd.thePort); //• Quickdraw.
- InitFonts (); //• Font Manager.
- InitWindows (); //• Window Manager.
- InitMenus (); //• Menu Manager.
- TEInit (); //• TextEdit.
- InitDialogs (0L); //• Dialog manager.
- InitCursor (); //• Cursor handler.
- InitAllPacks (); //• Package Manager.
-
- SetUpMenus (); //• My routine to insert the menus.
- // hCurs = *GetCursor (256); //• Load the Crosshairs cursor that I use in the game.
- // hCurs2 = *GetCursor (257); //• Load the Crosshairs cursor that I use in the game.
- // crosshairs = hCurs[0][0];
- // crosshairs2 = hCurs2[0][0];
- // SetCursor (crosshairs);
- // cityPattern = GetPattern (256);
-
- screenRect = qd.screenBits.bounds; //• Don't actually use this, but might later.
-
- doneFlag = false; //• This flag is set to False when user selects 'Quit'.
- highScore = 0;
-
- gameSpeed = 8; //• Set up all the user-settable options.
- startRound = 0;
- mFlag1 = true;
- mFlag2 = 1;
- for (i = 1; i <= 3; i++)
- {
- MExists[i] = true;
- MPBase[i] = 10 * i;
- MPExtra[i] = 10 * i;
- }
-
- //• myWindow := GetNewWindow (256, &wRecord, (WindowPtr)-1L);.
- wRect = screenRect;
- wRect.top = 20;
- myWindow = NewWindow (&wRecord, &wRect,
- "\pMissile Command by Robert Munafo",
- true, 0, (WindowPtr)-1L, false, 0);
-
- SetPort (myWindow); //• Might as well play the game in a window.
-
- //• These variables are used by the game.
- fieldWidth = myWindow->portRect.right;
- fieldHeight = myWindow->portRect.bottom;
-
- TextFont (0);
- TextFace (0);
-
- InitFbs (); //• Set up the fireball BitMaps.
-
- second_string = GetString (str_2_ID); //• Get strings from resource.
- first_string = GetString (str_1_ID);
-
- FlushEvents (everyEvent, 0);
- }
-
- //• %Subtitle 'Gets a text string'.
-
- //• This routine conducts a dialog to get a text string from the.
- //• user. The function value returned indicates whether the user stopped by entering.
- //• OK or cancel. The ID is the dialog ID number to use and Text is the.
- //• text they typed in. To get the text, we display the dialog window,.
- //• get the handle of the edit text and execute a small event loop wich waits.
- //• until one of the two buttons is pressed. When done, we dispose of the dialogue.
- //• window, set the flag depending on the means of exiting and return the text.
- //• The predefined dialogue window has the following items in it:.
-
- //• Item Number Item.
- //• 1 OK button, Enabled.
- //• 2 Cancel button, Enabled.
- //• 3 EditText area, Enabled.
- //• 4 StatText area, Disabled (Prompt).
- //• 5. Anything else, Disabled.
- //• *)
-
- //• the actual function has been deleted.
-
- //• %subtitle '"About Missile" routine'.
- //• This routine is nearly identical to Jason Ansley's About_Windows routine.
- void AboutMissile ()
- {
- short item_chosen; //• which botton was hit (I only have one so it doesn't really matter).
- short item_type; //• type of item in res. def. file (needed to pass as parameter).
- Handle first_handle, second_handle; //• handles to my items.
- DialogPtr the_dialog; //• a pointer to my dialog box.
- Rect box; //• the display rectangle of the item.
-
- the_dialog = GetNewDialog (box_ID, 0L, (WindowPtr)-1L); //• get dialog box from resource file.
-
- GetDItem (the_dialog, 2, &item_type, &first_handle, &box); //• get text format info from file.
- GetDItem (the_dialog, 3, &item_type, &second_handle, &box);
-
- SetIText (first_handle, &first_string[0][0]); //• set the lines of text into the box.
- SetIText (second_handle, &second_string[0][0]);
-
- ModalDialog (0L, &item_chosen); //• operate the box.
- DisposDialog (the_dialog); //• get rid of the box now that I'm done with it.
- }
-
- //• %subtitle 'My own random number routine'.
- Fixed Rnd (Fixed n)
- {
- return ((abs (Random ()) % n) + 1);
- }
-
- //• %subtitle 'DrawNumber - converts number to string and draws it on the screen'.
- void DrawNumber (long n)
- {
- Str255 s;
-
- NumToString (n, s);
- DrawString (s);
- }
-
- //• %subtitle 'Update the score at the bottom of the screen'.
- void UpdScore ()
- {
- TextMode (srcBic);
- MoveTo (scoreNumH, fieldHeight - 5);
- TextSize (24);
- DrawNumber (disScore);
-
- TextMode (srcOr);
- MoveTo (scoreNumH, fieldHeight - 5);
- DrawNumber (score);
- disScore = score;
-
- EraseRect (&statsRect);
- TextMode (srcOr);
- MoveTo (statsNumH, fieldHeight - 20);
- TextSize (12);
- DrawNumber (eLeft);
- MoveTo (statsNumH, fieldHeight - 5);
- DrawNumber (yLeft);
- TextMode (srcCopy);
- MoveTo (eDesNumH, fieldHeight - 20);
- DrawNumber (eDestroyed);
- }
-
- //• %subtitle 'Print the score'.
- void DrawScore ()
- {
- TextMode (srcCopy);
- MoveTo (scoreH, fieldHeight - 14);
- TextSize (12);
- DrawString ("\pScore: ");
- MoveTo (scoreNumH, fieldHeight - 5);
- TextSize (24);
- DrawNumber (score);
- disScore = score;
-
- TextSize (12);
- MoveTo (statsH, fieldHeight - 20);
- DrawString ("\p\pEnemy left: ");
- DrawNumber (eLeft);
- MoveTo (statsH, fieldHeight - 5);
- DrawString ("\pYours left: ");
- MoveTo (statsNumH, fieldHeight - 5);
- DrawNumber (yLeft);
- MoveTo (eDesH, fieldHeight - 20);
- DrawString ("\pDestroyed: ");
- MoveTo (eDesNumH, fieldHeight - 20);
- DrawNumber (eDestroyed);
- }
-
- //• %subtitle 'Print round #, score, and # killed'.
- void BottomLine ()
- {
- TextSize (12);
- RoundH = 10 + StringWidth ("\pRound: ");
- scoreH = RoundH + 2 * StringWidth ("\p00 ");
- scoreNumH = scoreH + StringWidth ("\pScore: ");
- eDesH = fieldWidth - StringWidth ("\pDestroyed: 0000 ");
- eDesNumH = fieldWidth - StringWidth ("\p0000 ");
- statsH = eDesH - StringWidth ("\pEnemy left: 000 ");
- statsNumH = eDesH - StringWidth ("\p000 ");
- SetRect (&statsRect, statsNumH, fieldHeight - 30, eDesH - 2, fieldHeight);
-
- TextMode (srcCopy);
- MoveTo (10, fieldHeight - 14);
- DrawString ("\pRound: ");
- MoveTo (RoundH, fieldHeight - 5);
- TextSize (24);
- DrawNumber (roundNumber);
-
- DrawScore ();
- MoveTo (eDesH, fieldHeight - 5);
- DrawString ("\pHigh score: ");
- DrawNumber (highScore);
- }
-
- //• %subtitle 'Create a new fireball'.
- void AllocFB (Point location)
- {
- short i;
- short idlefb;
-
- idlefb = 0;
- for (i = 1; 1 <= maxFb; i++)
- if ((fbs[i].mode == fbIdle) && (idlefb == 0))
- idlefb = i;
-
- if (idlefb != 0)
- {
- SetRect (&fbs[idlefb].bounds, location.h - 30, location.v - 30, location.h + 30, location.v + 30);
- fbs[idlefb].size = 0;
- fbs[idlefb].mode = fbGrow;
- }
- }
- //• of AllocFB.
-
- //• %subtitle 'Advance (animate) the fireballs'.
- void AdvanFb ()
- {
- short i;
- Rect aRect;
- Rect fbRect; //• Rectangle for drawing fireballs.
-
- PenSize (fbRadRate, fbRadRate);
- PenPat (&qd.black);
- SetRect (&aRect, 0, 0, 60, 60);
- for (i = 1; i < maxFb; i++)
- {
- if (fbs[i].mode != fbIdle)
- switch (fbs[i].mode)
- {
- case fbGrow:
- {
- fbs[i].size = fbs[i].size + 1;
- fbBitMap.baseAddr = (Ptr) &fbBits[240 * fbs[i].size - 239]; //• Select a fireball picture.
-
- CopyBits ((BitMap*) &fbBitMap,
- &qd.thePort->portBits,
- &aRect,
- &fbs[i].bounds, srcOr, 0L);
-
- if (fbs[i].size >= fbRad)
- fbs[i].mode = fbShrink;
- }
- break;
-
- case fbShrink:
- {
- fbBitMap.baseAddr = (Ptr) &fbBits[240 * fbs[i].size - 239]; //• Select the correct fireball picture.
-
- CopyBits ((BitMap*) &fbBitMap,
- &qd.thePort->portBits,
- &aRect,
- &fbs[i].bounds, srcBic, 0L);
-
- fbs[i].size = fbs[i].size - 1;
- if (fbs[i].size == 0)
- fbs[i].mode = fbIdle;
- }
- break;
- }
- } //• of loop.
- }
- //• of AdvanFb.
-
- //• %subtitle 'Create a new enemy missile'.
- void AllocMS (Point position, short city, short kind)
- {
- short i;
- short idleMs;
- short targetCity;
- short targetH;
-
- idleMs = 0;
- for (i = 1; i <= maxMs; i++)
- if ((missiles[i].mode == msIdle) && (idleMs == 0))
- idleMs = i;
-
- if (idleMs != 0)
- {
- i = idleMs;
- missiles[i].start.v = position.v; //• Set coordinates for beginning of path.
- missiles[i].start.h = position.h;
- missiles[i].pos.v = missiles[i].start.v * 16; //• Initial current position is.
- missiles[i].pos.h = missiles[i].start.h * 16; //• at start of path.o.
- missiles[i].dv = msSpeed; //• Speed of missile.
- missiles[i].city = city;
- targetH = (((missiles[i].city * 2 - 1) * fieldWidth) / (2 * nCities)) * 16;
- missiles[i].dh = (targetH - (missiles[i].start.h * 16)) / (((endv - missiles[i].start.v) * 16) / missiles[i].dv);
- missiles[i].kind = kind;
- missiles[i].mode = msActive;
- nMissiles = nMissiles + 1;
- }
- }
-
- Boolean BlackPixel (Point aPoint)
- {
- aPoint.h = aPoint.h / 16;
- aPoint.v = aPoint.v / 16;
- return ((GetPixel (aPoint.h, aPoint.v) &&
- GetPixel (aPoint.h + 1, aPoint.v)));
- }
-
- //• %subtitle 'Advance (animate) the enemy missiles'.
- void AdvanMs ()
- {
- short i, j;
- Point newPos, newPixel;
- Boolean detonate;
- short points;
-
- PenMode (patCopy);
- for (i = 1; i <= maxMs; i++)
- {
- if (missiles[i].mode != msIdle)
- {
- newPos.v = missiles[i].pos.v + missiles[i].dv;
- newPos.h = missiles[i].pos.h + missiles[i].dh;
- newPixel.v = newPos.v / 16;
- newPixel.h = newPos.h / 16;
- detonate = false;
-
- if (BlackPixel (newPos) || BlackPixel (missiles[i].pos))
- detonate = true;
-
- PenSize (mWidth, mWidth);
- PenPat (&qd.gray);
- MoveTo (missiles[i].pos.h / 16, missiles[i].pos.v / 16);
- LineTo (newPixel.h, newPixel.v);
- missiles[i].pos = newPos;
-
- //• If it's a Mirv, and it's reached the right altitude,.
- if ((missiles[i].kind == msMirv) &&
- (newPixel.v > mirvHeight))
- { //• then make lots of little missiles.
- for (j = 1; j <= nCities; j++)
- if ((j != missiles[i].city) && (Rnd (100) < mirvNasty))
- AllocMS (newPixel, j, msNormal);
- missiles[i].kind = msNormal;
- }
- //• If the missile has reached its target or
- //• hit a fireball...
- if ((newPos.v / 16 >= endv) || detonate)
- {
- PenSize (mWidth + 2, mWidth + 2);
- PenPat (&qd.white);
- MoveTo (missiles[i].start.h - 1, missiles[i].start.v - 1); //• Erase the line.
- LineTo (newPixel.h - 1, newPixel.v - 1);
- missiles[i].mode = msIdle; //• turn off the missile,.
- nMissiles = nMissiles - 1;
- if (detonate)
- {
- if ((mFlag2 == 2) || ((mFlag2 == 1) && (Rnd (25) > roundNumber)))
- AllocFB (newPixel); //• Make a new fireball.
- eDestroyed = eDestroyed + 1;
- score = score + MPoints[missiles[i].kind];
- UpdScore ();
- }
- else
- if (cities[missiles[i].city])
- {
- AllocFB (newPixel);
- cities[missiles[i].city] = false;
- citiesLeft = citiesLeft - 1;
- if (citiesLeft + bCities == 0)
- gameOver = true;
- }
- } //• of detonate routine.
- } //• of THEN clause for this active missile.
- } //• of missile loop.
- }
- //• of AdvanMs.
-
- //• %subtitle 'Print a string in the center of the window'.
- void Centre (Str255 theText, short posV)
- {
- MoveTo ((fieldWidth - StringWidth (theText)) / 2, posV);
- DrawString (theText);
- }
-
- //• %subtitle 'Clear the screen'.
- void ClearScreen ()
- {
- Rect aRect;
-
- aRect.top = 0;
- aRect.left = 0;
- aRect.bottom = fieldHeight;
- aRect.right = fieldWidth;
- FillRect (&aRect, &qd.white);
- }
-
- //• %subtitle 'Draw a city'.
- void DrawCity (short city)
- {
- Point cityLoc;
- short bOffset;
- Rect aRect;
-
- qd.randSeed = 2; //• Make each city look the same.
- cityLoc.v = fieldHeight - cityHeight;
- cityLoc.h = ((city * 2 - 1) * fieldWidth) / (2 * nCities);
- bOffset = - (cityWidth / 2);
- while (bOffset < (cityWidth / 2))
- {
- aRect.left = cityLoc.h + bOffset;
- aRect.top = cityLoc.v - Rnd (BuildHeight);
- aRect.bottom = cityLoc.v + 2;
- aRect.right = aRect.left + buildWidth;
- FillRect (&aRect, &(**cityPattern));
- bOffset = bOffset + buildWidth;
- }
- }
-
- //• %subtitle 'Draw cities and bottom line'.
- void DrawStuff ()
- {
- short i;
- Rect aRect;
-
- ClearScreen ();
-
- for (i = 1; i <= nCities; i++)
- if (cities[i])
- DrawCity (i);
-
- PenPat (&qd.black);
- qd.randSeed = TickCount (); //• Randomize.
-
- BottomLine ();
- }
- //• of DrawStuff.
-
- //• %subtitle 'MinMax - Convert Num to String with range checking.
- short MinMax (Str255 theString, short minimum, short maximum)
- {
- long theNumber;
-
- StringToNum (theString, &theNumber);
- if (theNumber < minimum)
- theNumber = minimum;
- if (theNumber > maximum)
- theNumber = maximum;
- return (theNumber);
- }
-
- //• %subtitle 'DoGameOptions - Does the big dialog box'.
- void DoGameOptions ()
- {
- enum { //• Here are all the magic constants that go with that monster dialog box:.
- OK = 1, //• OK button.
- gSpeed = 5, //• Game Speed text box.
- sRound = 7, //• Start Round text box.
- MPB = 18, //• MPBase text boxes.
- MPE = 24, //• MPExtra text boxes.
- MF1 = 8, //• mFlag1 check box.
- MF2 = 10, //• mFlag2 radio buttons.
- MEX = 15 //• MExists check boxes.
- };
-
- GrafPtr savePort; //• For saving the GrafPort and restoring later.
- DialogRecord dStorage; //• Storage for my dialog box.
- DialogPtr dPtr; //• Pointer to my dialog box.
- short itemHit; //• Item that was just hit by the user.
- short theType; //• not used.
- Handle theHandle; //• Temp. handle.
- Rect theRect; //• not used.
- short theValue; //• Temp. integer value.
- short i; //• Loop Variable.
- short station; //• Current setting of radio buttons.
- Str255 theString; //• Temp. string.
- Str255 theString1 = {"\p+"}; //• Temp. string.
- Str255 theString2 = {"\p* round"};
-
- //• Load the data from disk.
- dPtr = GetNewDialog (box2_id, &dStorage, (WindowPtr)-1L);
-
- GetDItem (dPtr, gSpeed, &theType, &theHandle, &theRect); //• Set up all the EditText boxes.
- NumToString (gameSpeed, theString);
- SetIText (theHandle, (StringPtr) theString);
- GetDItem (dPtr, sRound, &theType, &theHandle, &theRect);
- NumToString (startRound, theString);
- SetIText (theHandle, (StringPtr) theString);
-
- for (i = 0; i < 2; i++)
- {
- GetDItem (dPtr, MPB + i, &theType, &theHandle, &theRect); //• Text for points each missile is worth.
- NumToString (MPBase[i + 1], theString);
- SetIText (theHandle, theString);
- GetDItem (dPtr, MPB + 3 + i, &theType, &theHandle, &theRect); //• Text for plus sign.
- // theString = '+';
- SetIText (theHandle, theString1);
- GetDItem (dPtr, MPE + i, &theType, &theHandle, &theRect); //• Text for additional points each round.
- NumToString (MPExtra[i + 1], theString);
- SetIText (theHandle, theString);
- GetDItem (dPtr, MPE + 3 + i, &theType, &theHandle, &theRect); //• text : '* round'.
- // theString = "\p* round";
- SetIText (theHandle, theString2);
- GetDItem (dPtr, MEX + i, &theType, &theHandle, &theRect); //• Check boxes for types of missiles.
- theValue = 0;
- if (MExists[i + 1])
- theValue = 1;
- SetCtlValue ((ControlHandle) theHandle, theValue);
- }
- for (i = 0; i < 2; i++)
- { //• Program the radio buttons.
- GetDItem (dPtr, MF2 + i, &theType, &theHandle, &theRect);
- theValue = 0;
-
- if (mFlag2 == i)
- theValue = 1;
-
- SetCtlValue ((ControlHandle) theHandle, theValue);
- }
- station = MF2 + mFlag2;
-
- GetDItem (dPtr, MF1, &theType, &theHandle, &theRect); //• Set check box for mFlag1.
- theValue = 0;
-
- if (mFlag1)
- theValue = 1;
-
- SetCtlValue ((ControlHandle) theHandle, theValue);
-
- SelIText (dPtr, sRound, 0, 1000); //• Initial selection is startRound.
-
- do
- {
- ModalDialog (0L, &itemHit); //• Let them hit an item.
-
- if ((itemHit == 10) || //• Was it one of the radio buttons?.
- (itemHit == 11) ||
- (itemHit == 12))
- {
- GetDItem (dPtr, station, &theType, &theHandle, &theRect); //• Get the old one, and turn it off.
- SetCtlValue ((ControlHandle) theHandle, 0);
- station = itemHit;
- GetDItem (dPtr, station, &theType, &theHandle, &theRect); //• Turn this one on.
- SetCtlValue ((ControlHandle) theHandle, 1);
- }
- if ((itemHit == 8) || //• Was it a check box?.
- (itemHit == 15) ||
- (itemHit == 16) ||
- (itemHit == 17))
- {
- GetDItem (dPtr, itemHit, &theType, &theHandle, &theRect);
- theValue = 1 - GetCtlValue ((ControlHandle) theHandle); //• Find the value and invert it.
- SetCtlValue ((ControlHandle) theHandle, theValue);
- }
- } while (! (itemHit == 1) || ( itemHit == 2));
-
- if (itemHit == OK)
- { //• Have to get all the new values now!.
- GetDItem (dPtr, gSpeed, &theType, &theHandle, &theRect); //• Set up all the EditText boxes.
- GetIText ((Handle) theHandle, theString);
- gameSpeed = MinMax (theString, 1, 99);
-
- GetDItem (dPtr, sRound, &theType, &theHandle, &theRect);
- GetIText ((Handle) theHandle, theString);
- startRound = MinMax (theString, 1, 99);
-
- for (i = 0; i < 2; i++)
- {
- GetDItem (dPtr, MPB + i, &theType, &theHandle, &theRect); //• Text for points each missile is worth.
- GetIText (theHandle, theString);
- MPBase[i + 1] = MinMax (theString, -1000, 1000);
- GetDItem (dPtr, MPE + i, &theType, &theHandle, &theRect); //• Text for additional points each round.
- GetIText (theHandle, theString);
- MPExtra[i + 1] = MinMax (theString, -1000, 1000);
- GetDItem (dPtr, MEX + i, &theType, &theHandle, &theRect); //• Check boxes for types of missiles.
- if (GetCtlValue ((ControlHandle) theHandle) == 0)
- MExists[i + 1] = false;
- else
- MExists[i + 1] = true;
- }
- for (i = 0; i < 2; i++)
- { //• radio buttons.
- GetDItem (dPtr, MF2 + i, &theType, &theHandle, &theRect);
- if (GetCtlValue ((ControlHandle) theHandle) == 1)
- mFlag2 = i;
- }
- GetDItem (dPtr, MF1, &theType, &theHandle, &theRect); //• mFlag1.
- if (GetCtlValue ((ControlHandle) theHandle) == 0)
- mFlag1 = false;
- else
- mFlag1 = true;
- } //• if itemHit = OK.
- DisposDialog (dPtr);
- GetPort (&savePort); //• save whatever port was current.
- SetPort (myWindow);
- InvalRect (&screenRect); //• force the entire screen, including frame, to be redrawn.
- SetPort ((GrafPtr) savePort);
- }
- //• of DoGameOptions.
-
- //• %subtitle 'DoCommand - Does all the menu commands'.
- void DoCommand (long mResult)
- {
- Str255 name;
- short theMenu, theItem; //• Menu and item numbers.
- Boolean dummy;
-
- theMenu = HiWord (mResult);
- theItem = LoWord (mResult);
- switch (theMenu)
- {
-
- case appleMenu: //• About Missile and Desk Accessories.
- if (theItem == 1)
- AboutMissile ();
- else
- {
- GetItem (myMenus[1], theItem, name);
- refNum = OpenDeskAcc (name);
- }
- break;
-
- case fileMenu: //• There is currently only one option in this menu.
- {
- doneFlag = true;
- pauseFlag = false;
- esFlag = false;
- }
- break;
-
- case editMenu: //• Cut, copy, paste, and undo.
- {
- dummy = SystemEdit (theItem - 1); //• Can't cut and paste in game window.
- } //• of editMenu.
- break;
-
- case gameMenu:
- {
- //• SetPort (myWindow);.
- switch (theItem)
- {
- case 1:
- pauseFlag = true; //• pause game.
- break;
-
- case 2:
- pauseFlag = false; //• resume game.
- break;
-
- case 3: //• Empty line in menu.
- break;
-
- case 4:
- DoGameOptions ();
- break;
-
- case 5:
- { //• New Game.
- gameOver = true;
- endDelay = 100;
- esFlag = false;
- pauseFlag = false;
- }
- break;
- }
- break; //• of item case.
- }
- break;
- } //• of menu case.
- HiliteMenu (0);
- }
- //• of DoCommand.
-
- //• %subtitle 'Routine to check for events'.
- void CheckEvents ()
- {
- Point mousePoint;
- short mouseCode;
- char theChar;
- Fixed i;
- Rect fbRect; //• Rectangle for drawing fireballs.
-
- do
- {
- SystemTask ();
-
- GetMouse (&mousePoint);
- LocalToGlobal (&mousePoint);
- mouseCode = FindWindow (mousePoint, &whichWindow);
-
- //• If my window is activated, set cursor:
- if (FrontWindow () == myWindow)
-
- //• If it's above my window, but not the scroll bar,
- //• the game is 'active',.my window is in front, and
- //• the user has missiles left, then:
- if ((whichWindow == myWindow) &&
- (mouseCode != inMenuBar) &&
- (! pauseFlag) &&
- (FrontWindow () == myWindow) &&
- (yLeft > 0))
- ;// SetCursor (crosshairs) //• Set the Missile Command cursor.
- else
- ;// SetCursor (crosshairs2); //• Otherwise, use an arrow.
-
- while (GetNextEvent (everyEvent, &myEvent))
- switch (myEvent.what)
- {
- case mouseDown:
- {
- code = FindWindow (myEvent.where, &whichWindow);
- switch (code)
- {
- case inMenuBar:
- DoCommand (MenuSelect (myEvent.where));
- break;
-
- case inSysWindow:
- SystemClick (&myEvent, whichWindow);
- break;
-
- //• Can't drag game window - it's too hard to.
- //• figure out collisions off-screen.
- case inDrag:
- break;
-
- case inGrow:
- case inContent:
- {
- if ((whichWindow != FrontWindow ()) && (whichWindow != myWindow)) //• Can't select myWindow - must close other windows.
- SelectWindow (whichWindow);
- else
- //• Game must be running.
- //• Make sure they have some missiles left.
- if ((! pauseFlag) && (FrontWindow () == myWindow) && (yLeft > 0)) //• Can't make fireballs while paused.
- {
- GlobalToLocal (&myEvent.where);
- if (myEvent.where.v > nukeHeight) //• If it's too low,.
- myEvent.where.v = nukeHeight; //• make it legal.
-
- //• Put a fireball in the list.
- AllocFB (myEvent.where);
-
- //• They have less missiles now.
- yLeft = yLeft - 1;
-
- //• let them know.
- UpdScore ();
- }
- }
- break;
- }
- break;
- }
- break;
-
- //• No Autokey - don't want to repeat Option-N.
- case keyDown:
- if (((myEvent.modifiers / 256) % 2 != 0)) //• If command key was held down.
- {
- theChar = (char) myEvent.message % 256;
- DoCommand (MenuKey (theChar));
- }
- break;
-
- case updateEvt:
- {
- SetPort (myWindow);
- BeginUpdate (myWindow);
- if (playing)
- {
- for (i = 1; i <= nCities; i++)
- if (cities[i])
- {
- DrawCity (i);
- }
- //• Randomize after drawing cities.
- qd.randSeed = TickCount ();
- for (i = 1; i <= maxMs; i++)
- if (missiles[i].mode == msActive)
- {
- PenSize (mWidth, mWidth);
- PenPat (&qd.gray);
- MoveTo (missiles[i].start.h, missiles[i].start.v);
- LineTo (missiles[i].pos.h / 16, missiles[i].pos.v / 16);
- }
- for (i = 1; i <= maxFb; i++)
- if (fbs[i].mode != fbIdle)
- {
- fbRect = fbs[i].bounds;
- InsetRect (&fbRect, 30 - 2 * fbs[i].size, 30 - 2 * fbs[i].size);
- PenMode (patCopy);
- FillOval (&fbRect, &qd.black);
- }
- }
- BottomLine ();
- EndUpdate (myWindow);
- }
- break;
- } //• of event case.
-
- //• If another window has been selected, don't do anything
- //• except process events until game window is re-selected. .
- } while (! (FrontWindow () == myWindow) && ! pauseFlag);
- }
- //• of CheckEvents.
-
- //• %subtitle 'Print the GAME OVER message'.
- void EndScreen ()
- {
- Rect aRect;
- short i;
- short GameOffset, OverOffset;
- Point Center;
-
- TextSize (72);
- TextMode (srcBic);
- PenMode (patOr);
- PenPat (&qd.black);
- PenSize (5, 5);
-
- GameOffset = StringWidth ("\pGAME") / 2;
- OverOffset = StringWidth ("\pOVER") / 2;
- Center.v = fieldHeight / 2;
- Center.h = fieldWidth / 2;
-
- SetRect (&aRect, Center.h, Center.v, Center.h, Center.v);
- for (i = 1; i <= 30; i++)
- {
- CheckEvents ();
- InsetRect (&aRect, -5, -5);
- FrameOval (&aRect);
- TextSize (72);
- TextMode (srcBic);
- Centre ("\pGAME", Center.v - 12);
- Centre ("\pOVER", Center.v + 60);
- }
- PenMode (patBic);
- for (i = 1; i <= 30; i++)
- {
- CheckEvents ();
- FrameOval (&aRect);
- InsetRect (&aRect, 5, 5);
- }
- }
- //• of EndScreen.
-
- void InitRound ()
- {
- short i;
- Boolean roundOver;
- short rMissiles; //• "round missiles" - number to set eLeft to each round.
- short yMissiles; //• "your Missiles" - similar to rMissiles.
-
- roundOver = false;
- endDelay = 0;
- playing = true;
- DrawStuff (); //• Redraw screen.
- for (i = 1; i < maxFb; i++)
- fbs[i].mode = fbIdle;
-
- for (i = 1; i < maxMs; i++)
- missiles[i].mode = msIdle;
-
- for (i = 1; i < 3; i++)
- MPoints[i] = MPBase[i] + MPExtra[i] * roundNumber;
-
- for (i = 1; i < nCities; i++)
- cities2[i] = cities[i];
-
- nMissiles = 0;
- mirvRate = 0;
- mirvHeight = fieldHeight / 4;
- mirvNasty = 30;
- msSpeed = 60;
-
- if (roundNumber > 2)
- mirvRate = 10;
- if (roundNumber > 4)
- msSpeed = 80;
- if (roundNumber > 6)
- mirvRate = 20;
- if (roundNumber > 8)
- mirvNasty = 50;
- if (roundNumber > 10)
- mirvHeight = fieldHeight / 3;
- if (roundNumber > 12)
- msSpeed = 90;
- if (roundNumber > 14)
- mirvRate = 30;
- if (roundNumber > 19)
- msSpeed = 100;
-
- msRate = 5 + roundNumber;
- rMissiles = (roundNumber * roundNumber) / 6 + roundNumber + 6; //• Compute # of enemy missiles.
- yMissiles = rMissiles + roundNumber; //• Adjust yMissiles accordingly.
- eLeft = rMissiles;
- yLeft = yMissiles;
- roundNumber = roundNumber + 1;
-
- if ((roundNumber % 5) == 0)
- bCities = bCities + 1;
-
- BottomLine ();
- FlushEvents (everyEvent, 0); //• Ignore unprocessed events from previous round.
- }
-
- void EndBonus ()
- {
- short i, points;
- Str255 aString = {"\pEnd of round "}, s;
-
- ClearScreen ();
- BottomLine ();
- TextSize (24);
- TextMode (srcCopy);
- // aString = "End of round ";
- aString[15] = (char) (48 + (roundNumber % 10));
-
- if (roundNumber > 9)
- aString[14] = (char) (48 + (roundNumber / 10));
-
- if (citiesLeft > 0)
- { //• give bonus points for cities.
- Centre (aString, 100);
- Centre ("\pBonus:", 130);
- points = 0;
- for (i = 1; i <= nCities; i++)
- if ((cities[i] && ! doneFlag))
- {
- lastTick = TickCount ();
- DrawCity (i);
- points = points + roundNumber * 20;
- score = score + roundNumber * 20;
- TextSize (24);
- NumToString (points, s);
- Centre (s, 160);
- UpdScore ();
- do
- {
- CheckEvents ();
- } while (! (TickCount () >= lastTick + 30) || doneFlag);
- }
- }
- if ((bCities > 0) && (citiesLeft < 6))
- { //• Give a bonus city.
- lastTick = TickCount ();
- TextSize (24);
- Centre ("\p* Bonus City *", 190);
- citiesLeft = citiesLeft + 1;
- bCities = bCities - 1;
- do
- {
- i = Rnd (6);
- } while (cities[i]);
- cities[i] = true;
- do
- {
- CheckEvents ();
- } while (! (TickCount () >= lastTick + 60) || doneFlag);
- }
- }
- //• of EndBonus.
-
- //• %subtitle 'Play a single game'.
- void PlayGame ()
- {
- short i;
- Str255 s;
- Point msPoint;
- short msCity, msKind;
- short rMissiles; //• "round missiles" - number to set eLeft to each round.
- short yMissiles; //• "your Missiles" - similar to rMissiles.
- Boolean roundOver;
- Str255 aString;
- short points;
-
- roundNumber = startRound; //• Start out at this round.
- score = 0;
- eDestroyed = 0;
-
- for (i = 1; i < nCities; i++)
- cities[i] = true;
- citiesLeft = nCities;
- bCities = 0;
-
- nukeHeight = fieldHeight - cityHeight - (BuildHeight / 2) - fbRad * fbRadRate;
- endv = fieldHeight - cityHeight - (BuildHeight / 2);
-
- gameOver = false;
- esFlag = true;
- lastTick = TickCount ();
-
- do
- { //• Repeat loop for playing rounds.
- InitRound ();
-
- do
- { //• Repeat loop for animating objects in game.
- CheckEvents ();
-
- //• If enough time has elapsed since the last update,
- //• update objects on screen.
- if (TickCount () >= lastTick + gameSpeed)
- {
- lastTick = TickCount ();
- if ((eLeft <= 0) && (nMissiles == 0))
- roundOver = true;
-
- if (roundOver || gameOver)
- endDelay = endDelay + 1;
- else if ((Rnd (100) < msRate) && (eLeft > 0))
- { //• Launch an enemy missile every now and then.
- eLeft = eLeft - 1;
- msPoint.v = 0;
- msPoint.h = Rnd (fieldWidth - 4);
- do
- {
- msCity = Rnd (nCities); //• Pick a city.
-
- //• Try again for most missiles if city was destroyed.
- } while (! ((Rnd (3) == 1) && mFlag1) || cities2[msCity]);
-
- msKind = 0;
- if (MExists[msNormal])
- msKind = msNormal;
- if ((Rnd (100) <= mirvRate) && MExists[msMirv])
- msKind = msMirv;
- if (msKind == 0)
- msKind = msNormal;
- AllocMS (msPoint, msCity, msKind);
- }
- AdvanFb (); //• Advance state of fireballs.
- AdvanMs (); //• Advance position of missiles.
- }
- } while (! ((gameOver || roundOver) && (endDelay > 30)) || doneFlag);
- playing = false;
- if (esFlag && (citiesLeft + bCities > 0) && ! doneFlag)
- EndBonus ();
- } while (! gameOver || doneFlag); //• End of loop to play rounds.
- if (score > highScore)
- highScore = score;
- if (esFlag)
- EndScreen ();
- }
- //• of PlayGame.
-
- //• %subtitle 'Main program'.
-
- void main ()
- { //• main program.
- SetUp ();
-
- do
- {
- PlayGame ();
- } while (! doneFlag);
- }
-
-